package image;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

public class ImageProcessing2 {
	
	public static void main(String[] args) {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		
		String image = "Disque";
		
		Mat m = null;
		Mat m_original = null;
		Mat m_draw = null;
		Mat grad_x = null;
		Mat grad_y = null;
		Mat outline = null;
		Mat grad_angle = null;

		int[] LUT_x = new int[360];
		int[] LUT_y = new int[360];
		double[] LUT = new double[360];
		int[] LUT_nb_points = new int[360];

		//Size size = new Size(640, 480);
		Size size = new Size(480, 360);
		
		int bary_i = 0;
		int bary_j = 0;
		
		// importer l'image de base
		m = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		m_original = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
		m_draw = Highgui.imread("image/"+image+".png", Highgui.CV_LOAD_IMAGE_COLOR);
		m.convertTo(m, CvType.CV_64FC1);

		// espace de données entre 0 et 1
		for(int i=0; i<size.height; i++) {
			for(int j=0; j<size.width; j++) {
				m.put(i, j, m.get(i, j)[0]/255);
			}
		}
		
		// flouter
		Imgproc.GaussianBlur(m, m, new Size(9, 9), 0, 0);
		
		// calculer le contour
		List<MatOfPoint> liste_contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(m_original, liste_contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
		
		// parcourir les differents contours
		MatOfPoint contour  = liste_contours.get(0);
		//for(MatOfPoint contour : liste_contours) {
			bary_i = 0;
			bary_j = 0;
			
			// calcul du barycentre
			for(int i=0; i<contour.size().height; i++) {
				Core.circle(m_draw, new Point(contour.get(i, 0)), 1, new Scalar(0, 0, 255));
				bary_i += contour.get(i, 0)[0];
				bary_j += contour.get(i, 0)[1];
			}
			bary_i /= contour.size().height;
			bary_j /= contour.size().height;
			System.out.println("barycentre: "+bary_i+" "+bary_j);
			Core.circle(m_draw, new Point(bary_i, bary_j), 1, new Scalar(0, 0, 255));
			
			// calcul des gradients
			grad_x = Mat.zeros(size, CvType.CV_64FC1);
			grad_y = Mat.zeros(size, CvType.CV_64FC1);
			for(int i=1; i<size.height-1; i++) {
				for(int j=1; j<size.width-1; j++) {
					grad_y.put(i, j, m.get(i+1, j)[0] - m.get(i-1, j)[0]);
					grad_x.put(i, j, m.get(i, j+1)[0] - m.get(i, j-1)[0]);
					/*if(grad_x.get(i, j)[0] != 0)
						System.out.println(m.get(i, j+1)[0]+" "+m.get(i, j-1)[0]+" "+grad_x.get(i, j)[0]);*/
				}
			}
			
			// calcul des lookUpTables
			outline = Mat.zeros(size, CvType.CV_64FC1);
			grad_angle = Mat.zeros(size, CvType.CV_64FC1);
			for(int i=0; i<size.height; i++) {
				for(int j=0; j<size.width; j++) {
					double ix = -grad_x.get(i, j)[0];
					double iy = -grad_y.get(i, j)[0];
					
					if(Math.pow(ix, 2) + Math.pow(iy, 2) > 0.1) {
		    			outline.put(i, j, 1);
		    			
		    			double grad_norm_angle_origine = Math.atan2(iy,ix)/(2*Math.PI);
		    			//System.out.print(grad_norm_angle_origine+" ");
		    			double grad_norm_angle = (grad_norm_angle_origine+1) % (double) 1;
		    			//System.out.print(grad_norm_angle+" ");
		    			
		    			int grad_angle_in_degree = (int) (grad_norm_angle*360);
		    			grad_angle.put(i, j, grad_norm_angle);
		    			//System.out.println(grad_angle_in_degree);

		    			LUT_x[grad_angle_in_degree] += (j- (int) bary_i);
		    			LUT_y[grad_angle_in_degree] += (i- (int) bary_j);
		    			LUT_nb_points[grad_angle_in_degree]++;
		    			
		    			if(grad_angle_in_degree == 0) {
		    				System.out.println("! "+j+" "+(int) bary_j+" "+(j- (int) bary_j)+" "+LUT_x[grad_angle_in_degree]);
		    				System.out.println("! "+i+" "+(int) bary_i+" "+(i- (int) bary_i)+" "+LUT_y[grad_angle_in_degree]);
		    			}
					}
				}
			}
			
			try {
				FileWriter fw = new FileWriter(new File(image+"_"+contour.size().height));
				BufferedWriter writer = new BufferedWriter(fw);
				for(int i = 0;i<360; i++) {
			    	if(LUT_nb_points[i]>0) {
			    		double bary_angle_norm_original = Math.atan2(LUT_y[i]/LUT_nb_points[i], LUT_x[i]/LUT_nb_points[i]) / (2*Math.PI);
			    		double bary_angle_norm = (bary_angle_norm_original+1) % (double) 1;
			    		System.out.println(i+" "+(int) LUT_x[i]+" "+(int) LUT_y[i]+" "+(int) LUT_nb_points[i]+" "+LUT_x[i]/LUT_nb_points[i]+" "+LUT_y[i]/LUT_nb_points[i]
			    				+" "+Math.atan2(LUT_y[i]/LUT_nb_points[i], LUT_x[i]/LUT_nb_points[i])+" "+bary_angle_norm_original);
			    		
			    		LUT[i] = bary_angle_norm*360;
			    		writer.write(i+" "+(int) LUT[i]);
			    		writer.newLine();
			    		
			    		//System.out.println(bary_angle_norm_original+" "+bary_angle_norm+" "+LUT[i]);
			    	} else {
			    		LUT[i] = -1;
			    	}
			    	
			    }
			    writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		//}
		
		new LoadImage("image/"+image+"_mod.png", m_draw, "Original", false, 0, 0);
		new LoadImage("image/"+image+"_grad_x.png", grad_x, "Gradient x", true, 0, 0);
		new LoadImage("image/"+image+"_grad_y.png", grad_y, "Gradient y", true, 0, 0);
		new LoadImage("image/"+image+"_dir_grad.png", grad_angle, "Direction Gradient", true, 0, 0);
	}
	
}
